let cp = require("child_process"),
  schedule = require("node-schedule"),
  fs = require("fs");
module.exports = async function (app, models, $, db, nm) {
  let DB = {
    dbs: {},
    async get(e) {
      if (!this.dbs[e]) {
        let t = await models.datasource.findSync({
          id: e,
          status: 1
        });
        if (t.errorMsg) return;
        t.database = t.dbn, this.dbs[e] = nm.DB(t)
      }
      return this.dbs[e]
    },
    async eval(e, t, a) {
      let n = await this.get(e),
        i = "string" == typeof (a = a || {}).cache ? a.cache : $.MD5(t);
      if (a.cache && nm.redis && nm.redis.cache) try {
        return JSON.parse(await nm.redis.cache.get(i))
      } catch (e) {}
      let r = n ? await n._evalSync(t, a) : {
        errorMsg: "datasource err"
      };
      return !r.errorMsg && a.cache && nm.redis && nm.redis.cache && (await nm.redis.cache.set(i, JSON.stringify(r)), await nm.redis.cache.expire(i, a.cacheTime || 2592e3)), r
    }
  };
  async function EvalString(string, pFunction) {
    try {
      string = string.replace(/`([a-z,A-Z,0-9]+)`/g, "$1");
      let now = new Date,
        currentday, yesterday = new Date(now),
        currentmonth, lastmonth = new Date(now),
        currentyear = now.getFullYear(),
        lastyear = now.getFullYear() - 1;
      return currentday = $.date().Format("yyyy-MM-dd"), yesterday.setDate(now.getDate() - 1), yesterday = $.date(yesterday).Format("yyyy-MM-dd"), currentmonth = $.date(now).Format("yyyy-MM"), lastmonth.setDate(0), lastmonth.setMonth(lastmonth.getMonth() - 1), lastmonth = $.date(lastmonth).Format("yyyy-MM"), pFunction = `\n                ${pFunction || ""}\n                string = \`${string}\`\n            `, eval(pFunction), string
    } catch (e) {
      throw console.log(pFunction, string), e
    }
  }
  async function evalJob(id) {
    let job = await models.job.findSync({
      id: id
    }, {
      join: {
        pjectId: {
          type: "LEFT",
          name: "pject",
          key: "id",
          projection: {
            name: "pjectName"
          }
        }
      }
    });
    return job.errorMsg ? job : new Promise(async next => {
      async function _next(e) {
        await models.log.insertSync({
          level: e.errorMsg ? 2 : 1,
          type: `${job.pjectName}`,
          desoptions: `${job.name}[${job.id}]-${job.type}-job`,
          content: e.errorMsg || e.msg || ""
        }), next(e)
      }
      let cmd = job.content,
        filePath = "",
        fileScript = job.file ? await EvalString(decodeURI(job.file), job.pFunction) : "";
      if (!job.file || fileScript) {
        if ("datax" == job.type) try {
          filePath = __dirname.replace("router", "uploads/") + job.name + "_" + id + ".json", fs.writeFileSync(filePath, fileScript), cmd = Config.dataxCMD + filePath
        } catch (e) {
          return void _next({
            errorMsg: "执行失败,配置解析出错",
            msg: e.message
          })
        } else if ("datay" == job.type) try {
          console.log($.date().Format("yyyy-MM-dd hh:mm:ss"), "DataY任务:", fileScript);
          let e = JSON.parse(fileScript),
            t = await DB.eval(e.reader.sourceId, e.reader.querySql);
          e.writer.preSql && await DB.eval(e.writer.sourceId, e.writer.preSql);
          let a = [];
          for (let n = 0; n < t.length; n++) {
            let i = [];
            if ("*" == e.writer.column[0])
              for (let e in t[n]) {
                let a = t[n][e],
                  r = a && a.indexOf && a.indexOf('"') > -1 ? "'" : '"';
                null != a && i.push(`${e}=${r}${a}${r}`)
              } else
                for (let a = 0; a < e.writer.column.length; a++) {
                  let r = e.writer.column[a],
                    s = t[n][r],
                    o = s && s.indexOf && s.indexOf('"') > -1 ? "'" : '"';
                  null != s && i.push(`${r}=${o}${s}${o}`)
                }
            let r = await DB.eval(e.writer.sourceId, `INSERT INTO ${e.writer.table} SET ${i.join(",")};`);
            r.errorMsg && a.push(r.errorMsg)
          }
          return void _next({
            msg: `执行完成,共:${t.length},失败:${a.length}`
          })
        } catch (e) {
          return console.log(e), void _next({
            errorMsg: "执行失败",
            msg: e.message
          })
        }
        if ("code" != job.type) "command" == job.type && fileScript && (filePath = "./.cache.command.sh", cmd = `${filePath} ${process.pid}`, fs.writeFileSync(filePath, fileScript), filePath = ""), cp.exec(cmd, async (e, t, a) => {
          console.log(t, a), filePath && fs.unlinkSync(filePath), _next(e ? {
            errorMsg: "执行失败",
            msg: t
          } : {
            msg: t
          })
        });
        else try {
          eval(`async function evalFn(){\n                        ${fileScript}\n                    }`), _next(await evalFn() || {
            msg: "执行成功"
          })
        } catch (e) {
          console.log(e), _next({
            errorMsg: "执行失败,脚本运行出错",
            msg: e.message
          })
        }
      } else _next({
        errorMsg: "执行失败,脚本构建出错"
      })
    })
  }
  app.post("/v1/datasource/test", async function (e, t) {
    "admin" == e.session.userName ? t.send(e.body.id ? await DB.eval(e.body.id, e.body.content || "SHOW TABLES;") : {
      errorMsg: "参数错误"
    }) : t.send({
      errorMsg: "无权限"
    })
  }), app.get("/v1/dataset", async function (e, t) {
    let a = e.query;
    if (!a.id) return void t.send({
      errorMsg: "参数错误"
    });
    let n = await models.dataset.findSync({
      id: a.id,
      status: 1
    });
    if (n.errorMsg) t.send(n);
    else {
      for (let e in a) n.content = n.content.replace("${" + e + "}", a[e]), n.content = n.content.replace(new RegExp("#" + e, "g"), a[e]);
      if (n.content = await EvalString(n.content), a.pageNum || a.pageSize) {
        pageing = !0;
        let e = a.pageNum || 1,
          i = a.pageSize || 10,
          r = n.content.replace(/;$/, ""),
          s = r.replace(/\n/g, " ").replace(/^select [^from]+ from/im, "select count(*) as total from");
        s = await DB.eval(n.datasourceId, s);
        let o = `${r} LIMIT ${i} OFFSET ${i * (e - 1)}`;
        o = await DB.eval(n.datasourceId, o), t.send({
          total: s[0].total,
          rows: o
        })
      } else t.send(await DB.eval(n.datasourceId, n.content))
    }
  }), app.get("/v1/job/:id", async (e, t) => {
    let a = e.params.id;
    a ? t.send(await evalJob(a)) : t.send({
      errorMsg: "参数错误"
    })
  });
  let Task = {
    ids: {},
    async start(e) {
      try {
        return this.ids[e.id] && this.ids[e.id].cancel(), this.ids[e.id] = schedule.scheduleJob(e.cron, async function () {
          await evalJob(e.id)
        }), console.log(`任务${e.name}-${e.id}启动完成`), {
          msg: `任务${e.name}-${e.id}启动完成`
        }
      } catch (t) {
        return console.log(`任务${e.name}-${e.id}启动失败`, t), {
          errorMsg: `任务${e.name}-${e.id}启动失败`
        }
      }
    },
    async stop(e) {
      try {
        return this.ids[e.id] && this.ids[e.id].cancel(), console.log(`任务${e.name}-${e.id}停止完成`), {
          msg: `任务${e.name}-${e.id}停止完成`
        }
      } catch (t) {
        return console.log(`任务${e.name}-${e.id}停止失败`, t), {
          errorMsg: `任务${e.name}-${e.id}停止失败`
        }
      }
    }
  };
  async function getDataViewRouter(e, t, a) {
    let n = e.query;
    if (!n.id) return void t.send("参数错误");
    let i, r = await models.dataview.findSync({
      id: n.id,
      status: 1
    });
    if (r.errorMsg) t.send(r);
    else {
      if (r.datasourceId && r.content && !r.datasetId) i = {
        datasourceId: r.datasourceId,
        content: r.content
      };
      else if ((i = await models.dataset.findSync({
          id: r.datasetId,
          status: 1
        })).errorMsg) return void t.send(i);
      for (let e in n) i.content = i.content.replace("${" + e + "}", n[e]), i.content = i.content.replace(new RegExp("#" + e, "g"), n[e]);
      e.dataview = {
        name: r.name,
        option: r.deploy,
        dataset: await DB.eval(i.datasourceId, i.content)
      }, a()
    }
  }
  app.get("/v1/dataview", getDataViewRouter, async (req, res) => {
    let option, data = req.dataview.dataset;

    function getData(e, t) {
      let a = [];
      for (let n = 0; n < data.length; n++) {
        let i = data[n][e];
        a.push(t ? {
          value: i,
          name: data[n][t]
        } : i)
      }
      return a
    }
    eval(`${req.dataview.option}`), res.send(option)
  }), app.get("/dataview", (e, t, a) => {
    let n = e.query.token;
    e.headers.token = n, a()
  }, nm.loginRouter, getDataViewRouter, async (e, t) => {
    t.send(`\n        <!DOCTYPE html>\n        <html lang="zh-cmn-Hans">\n        <head>\n          <meta charset="UTF-8">\n          <meta content="yes" name="apple-mobile-web-app-capable">\n          <meta content="yes" name="apple-touch-fullscreen">\n          <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes">\n          <meta name="apple-mobile-web-app-status-bar-style" content="black">\n          <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0;">\n          <meta name="theme-color" content="#fff">\n          <title>${e.dataview.name}</title>\n          <style type="text/css">\n            html,body{\n              margin: 0;\n              padding: 0;\n              height: 100%;\n              width: 100%;\n            }\n          </style>\n            <script src="js/echarts.min.js"><\/script>\n        </head>\n        <body>\n          <div id="main" style="width: 100%;height:100%;"></div>\n          <script>\n            let data = ${JSON.stringify(e.dataview.dataset)}\n            function getData(valueKey,nameKey){\n              let rc = []\n              for(let i = 0; i < data.length; i ++){\n                let value = data[i][valueKey]\n                rc.push(nameKey ? { value, name:data[i][nameKey] } : value)\n              }\n              return rc\n            }\n            let mainDom = document.getElementById('main')\n            if(!data.length){\n                mainDom.innerHTML = '<div style="height:100%;display:flex;justify-content:center;align-items:center;color:#ccc;">${e.dataview.nullTips || "暂无数据～"}</div>'\n            }else{\n                var myChart = echarts.init(mainDom,'')\n                let option\n                ${e.dataview.option}\n                myChart.setOption(option);\n            }\n          <\/script>\n        </body>\n        </html>`)
  }), app.put("/v1/job/toggle", async (e, t) => {
    let a = e.body,
      n = a.status;
    a.name || (a = await models.job.findSync({
      id: a.id
    })), a.status = n, await models.job.updateSync({
      id: a.id
    }, {
      status: n
    }), t.send(a.status ? await Task.start(a) : Task.stop(a))
  });
  let jobs = await models.job.listSync({});
  for (let e = 0; e < jobs.length; e++) jobs[e].status && await Task.start(jobs[e]);
  return {
    DB: DB,
    evalJob: evalJob
  }
};